﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Xpedite.Resources.Definitions;
using Moq;
using Xpedite.Resources;
using Xpedite.Localization;

namespace Xpedite.Test.Resources
{
  [TestClass]
  public class ResourceContextTest
  {
    private readonly Mock<IResourceDefinitionFactory> _resourceDefinitionFactory = new Mock<IResourceDefinitionFactory>(MockBehavior.Strict);

    [TestMethod]
    public void TestCreateResourceDefinition()
    {
      var resources = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition = ExpectResourceDefinitionCreated("MyResource", resources);

      context.CreateResourceDefinition(ResourceType.Css, "MyResource", resources);

      Assert.AreEqual(1, context.Definitions.Count());
      Assert.AreSame(definition.Object, context.Definitions.First());
    }

    [TestMethod]
    public void TestCreateResourceDefinitionDuplicated()
    {
      var resources = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition = ExpectResourceDefinitionCreated("MyResource", resources);

      context.CreateResourceDefinition(ResourceType.Css, "MyResource", resources);
      context.CreateResourceDefinition(ResourceType.Css, "MyResource", resources);

      Assert.AreEqual(2, context.Definitions.Count());
      Assert.AreSame(definition.Object, context.Definitions.First());
      Assert.AreSame(definition.Object, context.Definitions.Skip(1).First());
    }

    [TestMethod]
    public void TestCreateOrExtendResourceDefinitionWhereNotFound()
    {
      var resources = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition = ExpectResourceDefinitionCreated("MyResource", resources);

      context.CreateOrExtendResourceDefinition(ResourceType.Css, "MyResource", resources);

      Assert.AreEqual(1, context.Definitions.Count());
      Assert.AreSame(definition.Object, context.Definitions.First());
    }

    [TestMethod]
    public void TestCreateOrExtendResourceDefinitionWhereFound()
    {
      var resources1 = CreateResourceCollection();
      var resources2 = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition1 = ExpectResourceDefinitionCreated("MyResource", resources1);
      var definition2 = ExpectResourceDefinitionExtended(definition1, resources2);

      context.CreateOrExtendResourceDefinition(ResourceType.Css, "MyResource", resources1);
      context.CreateOrExtendResourceDefinition(ResourceType.Css, "MyResource", resources2);

      Assert.AreEqual(1, context.Definitions.Count());
      Assert.AreSame(definition1.Object, context.Definitions.First());
    }

    [TestMethod]
    public void TestCreateOrExtendResourceDefinitionWhereDifferent()
    {
      var resources1 = CreateResourceCollection();
      var resources2 = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition1 = ExpectResourceDefinitionCreated("MyResource1", resources1);
      var definition2 = ExpectResourceDefinitionCreated("MyResource2", resources2);

      context.CreateOrExtendResourceDefinition(ResourceType.Css, "MyResource1", resources1);
      context.CreateOrExtendResourceDefinition(ResourceType.Css, "MyResource2", resources2);

      Assert.AreEqual(2, context.Definitions.Count());
      Assert.AreSame(definition1.Object, context.Definitions.First());
      Assert.AreSame(definition2.Object, context.Definitions.Skip(1).First());
    }

    [TestMethod]
    public void TestGetDefinitionWhereNotFound()
    {
      var context = new ResourceContext(_resourceDefinitionFactory.Object);

      Expect.Exception(() => context.GetDefinition("SomeName"), new XpediteException(String.Format(ExceptionMessages.UnknownDefinitionReferenceName, "SomeName")));
    }

    [TestMethod]
    public void TestGetDefinitionWhereFound()
    {
      var resources = CreateResourceCollection();
      var context = new ResourceContext(_resourceDefinitionFactory.Object);
      var definition = ExpectResourceDefinitionCreated("MyResource", resources);

      context.CreateResourceDefinition(ResourceType.Css, "MyResource", resources);

      Assert.AreSame(definition.Object, context.GetDefinition("MyResource"));
    }

    private IEnumerable<INamedValue<IResourceUrl>> CreateResourceCollection()
    {
      var resourceUrl = new Mock<IResourceUrl>(MockBehavior.Strict);
      
      return new[] { NamedValue.Create("SomeNamedResource" + resourceUrl.GetHashCode(), resourceUrl.Object) };
    }

    private Mock<IResourceDefinition> ExpectResourceDefinitionCreated(String resourceName, IEnumerable<INamedValue<IResourceUrl>> resources)
    {
      var definition = new Mock<IResourceDefinition>(MockBehavior.Strict);

      _resourceDefinitionFactory.Setup(mock => mock.Create(ResourceType.Css, resourceName)).Returns(definition.Object);
      definition.SetupGet(mock => mock.ReferenceName).Returns(resourceName);
      definition.Setup(mock => mock.ReferenceResources(resources));

      return definition;
    }

    private Mock<IResourceDefinition> ExpectResourceDefinitionExtended(Mock<IResourceDefinition> definition, IEnumerable<INamedValue<IResourceUrl>> resources)
    {
      definition.Setup(mock => mock.ReferenceResources(resources));

      return definition;
    }
  }
}
